﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace BasePlatformCom
{
	public class BaseDevice
	{
		public Random random = null;

		public int receiveTimeout = 100;		// 串口接收超时 - ms

		public byte[] memData = new byte[1024];
		public byte[] tmpData = new byte[1024];

		public int frameLength;
		public byte[] frameData = new byte[8192];
		public byte[] readBuffer = new byte[1024];

		public bool ackIsOk;
		public bool dataIsOk;
		public int readNum;
		public int waitTime;
		public int currentMainPort = ComMainPort.Unknown;

		// 读主控制器 Reg
		public void readMainReg(SerialComPort port, int regAddr, ref int regData)
		{
			frameLength = 8;

			frameData[0] = 0xAA;	// 同步低
			frameData[1] = 0x55;	// 同步高
			frameData[2] = 0x02;	// 命令
			frameData[3] = 0;		// 0
			frameData[4] = 0;		// 0
			frameData[5] = 0;		// 0
			frameData[6] = mod256(regAddr);	// 地址

			commandExec(port, 5 + 6, receiveTimeout);
			regData = readBuffer[5 + 4];
		}

		// 写主控制器 Reg
		public void writeMainReg(SerialComPort port, int regAddr, int regData)
		{
			frameLength = 516;

			frameData[0] = 0xAA;			// 同步低
			frameData[1] = 0x55;			// 同步高
			frameData[2] = 0x01;			// 命令
			frameData[3] = mod256(regAddr);	// 地址
			frameData[4] = mod256(regData);	// 数据

			for (int i = 5; i <= frameLength - 2; i++)
			{
				frameData[i] = 0xff;
			}

			commandExec(port, 5, receiveTimeout);
		}

		// 读主控制器 Memory
		public void readMainMem(SerialComPort port, int memAddr, byte[] memData)
		{
			for (int i = 0; i < 1024; i++)
			{
				memData[i] = 0;
			}

			frameLength = 8;

			frameData[0] = 0xAA;					// 同步低
			frameData[1] = 0x55;					// 同步高
			frameData[2] = 0x08;					// 命令
			frameData[3] = mod256(memAddr >> 24);
			frameData[4] = mod256(memAddr >> 16);
			frameData[5] = mod256(memAddr >> 8);
			frameData[6] = mod256(memAddr);		// 地址

			commandExec(port, 5 + 517, receiveTimeout);

			for (int i = 0; i < 256; i++)
			{
				memData[i] = readBuffer[9 + i];
			}
		}

		// 写主控制器 Memory
		public void writeMainMem(SerialComPort port, int memAddr, byte[] memData)
		{
			frameLength = 520;

			frameData[0] = 0xAA;					// 同步低
			frameData[1] = 0x55;					// 同步高
			frameData[2] = 0x04;					// 命令
			frameData[3] = mod256(memAddr >> 24);
			frameData[4] = mod256(memAddr >> 16);
			frameData[5] = mod256(memAddr >> 8);
			frameData[6] = mod256(memAddr);			// 地址

			for (int i = 0; i < 512; i++)
			{
				frameData[7 + i] = memData[i];
			}

			commandExec(port, 5, receiveTimeout);
		}

		// 读分控制器 Memory
		public bool readSubMem(SerialComPort port, int mainPort, int hubPort, int subIndex, int memAddr, byte[] memData)
		{
			int packetID = random.Next(32768);

			byte[] subCommData = new byte[1024];

			if (mainPort != currentMainPort)
			{
				if (mainPort == ComMainPort.U)
				{
					writeMainReg(port, 0x45, 0x03);
				}
				else if (mainPort == ComMainPort.D)
				{
					writeMainReg(port, 0x45, 0x05);
				}

				currentMainPort = (int)mainPort;
			}

			for (int i = 0; i < 512; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 0; i < 6; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 6; i < 12; i++)
			{
				subCommData[i] = 0;
			}

			subCommData[12] = 0x55;
			subCommData[13] = 0xAA;
			subCommData[14] = 0x81;
			subCommData[15] = 0xC5;
			subCommData[16] = 0x01;
			subCommData[17] = 0x00;
			subCommData[18] = 0x00;
			subCommData[19] = 0x00;
			subCommData[20] = 0x00;
			/************ 命令地址 ***********/
			subCommData[21] = 0x00;
			subCommData[22] = mod256(subIndex);		// 分控级联深度 0~255
			subCommData[23] = mod64(hubPort);		// 分线器端口号 0~63, 分控/附属设备通用
			subCommData[24] = 0x00;					// 附属设备级联深度 0~63
			/************ 命令地址 ***********/
			subCommData[25] = 0x08;
			subCommData[26] = 0x01;
			subCommData[27] = mod256(packetID);
			subCommData[28] = mod256(packetID >> 8);
			subCommData[29] = 0x08;
			// Memory 读命令
			subCommData[30] = 0x00;
			subCommData[31] = mod256(memAddr);
			subCommData[32] = mod256(memAddr >> 8);
			subCommData[33] = mod256(memAddr >> 16);
			subCommData[34] = mod256(memAddr >> 24);
			for (int i = 35; i < 35 + 256; i++)
			{
				subCommData[i] = 0;
			}

			writeMainMem(port, 0x0A00, subCommData);
			
			if (mainPort == ComMainPort.U)
			{
				readMainMem(port, 0x1200, tmpData);
			}
			else if (mainPort == ComMainPort.D)
			{
				readMainMem(port, 0x1A00, tmpData);
			}

			for (int i = 0; i < 256; i++)
			{
				memData[i] = tmpData[27 - 9 + i];
			}

			// 根据包编号判断读操作的有效性
			if (subCommData[27] != readBuffer[19] || subCommData[28] != readBuffer[20])
			{
				return false;
			}
			else
			{
				return true;
			}
		}

		// 写分控制器 Memory
		public void writeSubMem(SerialComPort port, int mainPort, int subIndex, int memAddr, byte[] memData)
		{
			int packetID = random.Next(32768);

			byte[] subCommData = new byte[1024];

			if (mainPort != currentMainPort)
			{
				if (mainPort == ComMainPort.U)
				{
					writeMainReg(port, 0x45, 0x03);
				}
				else if (mainPort == ComMainPort.D)
				{
					writeMainReg(port, 0x45, 0x05);
				}

				currentMainPort = (int)mainPort;
			}

			for (int i = 0; i < 512; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 0; i < 6; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 6; i < 12; i++)
			{
				subCommData[i] = 0;
			}

			subCommData[12] = 0x55;
			subCommData[13] = 0xAA;
			subCommData[14] = 0x81;
			subCommData[15] = 0xC5;
			subCommData[16] = 0x01;
			subCommData[17] = 0x00;
			subCommData[18] = 0x00;
			subCommData[19] = 0x00;
			subCommData[20] = 0x00;

			if (subIndex == 0)
			{
				subCommData[21] = 0x01;
				subCommData[22] = 0x00;				// 全局分控地址
			}
			else
			{
				subCommData[21] = 0x00;
				subCommData[22] = mod256(subIndex);	// 定点分控地址
			}
			subCommData[23] = 0x00;					// 分线器端口号 0~63, 分控/附属设备通用
			subCommData[24] = 0x00;
			
			subCommData[25] = 0x08;
			subCommData[26] = 0x01;
			
			subCommData[27] = mod256(packetID);
			subCommData[28] = mod256(packetID >> 8);
			subCommData[29] = 0x04;

			// Memory 写命令
			subCommData[30] = 0x00;
			subCommData[31] = mod256(memAddr);
			subCommData[32] = mod256(memAddr >> 8);
			subCommData[33] = mod256(memAddr >> 16);
			subCommData[34] = mod256(memAddr >> 24);

			// 负载数据
			for (int i = 0; i < 256; i++)
			{
				subCommData[35 + i] = memData[i];
			}

			writeMainMem(port, 0x0A00, subCommData);
		}

		// 读附属设备 Memory
		public bool readEquMem(SerialComPort port, int mainPort, int hubPort, int equIndex, int memAddr, byte[] memData)
		{
			int packetID = random.Next(32768);

			byte[] subCommData = new byte[1024];

			if (mainPort != currentMainPort)
			{
				if (mainPort == ComMainPort.U)
				{
					writeMainReg(port, 0x45, 0x03);
				}
				else if (mainPort == ComMainPort.D)
				{
					writeMainReg(port, 0x45, 0x05);
				}

				currentMainPort = (int)mainPort;
			}

			for (int i = 0; i < 512; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 0; i < 6; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 6; i < 12; i++)
			{
				subCommData[i] = 0;
			}

			subCommData[12] = 0x55;
			subCommData[13] = 0xAA;
			subCommData[14] = 0x81;
			subCommData[15] = 0xC5;
			subCommData[16] = 0x01;
			subCommData[17] = 0x00;
			subCommData[18] = 0x00;
			subCommData[19] = 0x00;
			subCommData[20] = 0x00;
			/************ 命令地址 ***********/
			subCommData[21] = 0x00;
			subCommData[22] = 0x00;					// 分控级联深度 0~255
			subCommData[23] = mod64(hubPort);		// 分线器端口号 0~63, 分控/附属设备通用
			subCommData[24] = mod64(equIndex);		// 附属设备级联深度 0~63
			/************ 命令地址 ***********/
			subCommData[25] = 0x08;
			subCommData[26] = 0x01;
			subCommData[27] = mod256(packetID);
			subCommData[28] = mod256(packetID >> 8);
			subCommData[29] = 0x08;
			// Memory 读命令
			subCommData[30] = 0x00;
			subCommData[31] = mod256(memAddr);
			subCommData[32] = mod256(memAddr >> 8);
			subCommData[33] = mod256(memAddr >> 16);
			subCommData[34] = mod256(memAddr >> 24);
			for (int i = 35; i < 35 + 256; i++)
			{
				subCommData[i] = 0;
			}

			writeMainMem(port, 0x0A00, subCommData);
			if (mainPort == ComMainPort.U)
			{
				readMainMem(port, 0x1200, tmpData);
			}
			else if (mainPort == ComMainPort.D)
			{
				readMainMem(port, 0x1A00, tmpData);
			}

			for (int i = 0; i < 256; i++)
			{
				memData[i] = tmpData[27 - 9 + i];
			}

			// 根据包编号判断读操作的有效性
			if (subCommData[27] != readBuffer[19] || subCommData[28] != readBuffer[20])
			{
				return false;
			}
			else
			{
				return true;
			}
		}

		// 写附属设备 Memory
		public void writeEquMem(SerialComPort port, int mainPort, int equIndex, int memAddr, byte[] memData)
		{
			int packetID = random.Next(32768);

			byte[] subCommData = new byte[1024];

			if (mainPort != currentMainPort)
			{
				if (mainPort == ComMainPort.U)
				{
					writeMainReg(port, 0x45, 0x03);
				}
				else if (mainPort == ComMainPort.D)
				{
					writeMainReg(port, 0x45, 0x05);
				}

				currentMainPort = (int)mainPort;
			}

			for (int i = 0; i < 512; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 0; i < 6; i++)
			{
				subCommData[i] = 255;
			}

			for (int i = 6; i < 12; i++)
			{
				subCommData[i] = 0;
			}

			subCommData[12] = 0x55;
			subCommData[13] = 0xAA;
			subCommData[14] = 0x81;
			subCommData[15] = 0xC5;
			subCommData[16] = 0x01;
			subCommData[17] = 0x00;
			subCommData[18] = 0x00;
			subCommData[19] = 0x00;
			subCommData[20] = 0x00;

			subCommData[21] = 0x00;
			subCommData[22] = 0x00;
			subCommData[23] = 0x00;					// 分线器端口号, 分控/附属设备通用
			if (equIndex == 0)
			{
				subCommData[21] = 0x02;
				subCommData[24] = 0x00;				// 全局附属设备级联深度
			}
			else
			{
				subCommData[24] = mod64(equIndex);	// 定点附属设备级联深度
			}
			
			subCommData[25] = 0x08;
			subCommData[26] = 0x01;

			subCommData[27] = mod256(packetID);
			subCommData[28] = mod256(packetID >> 8);
			subCommData[29] = 0x04;

			// Memory 写命令
			subCommData[30] = 0x00;
			subCommData[31] = mod256(memAddr);
			subCommData[32] = mod256(memAddr >> 8);
			subCommData[33] = mod256(memAddr >> 16);
			subCommData[34] = mod256(memAddr >> 24);

			// 负载数据
			for (int i = 0; i < 256; i++)
			{
				subCommData[35 + i] = memData[i];
			}

			writeMainMem(port, 0x0A00, subCommData);
		}

		#region 串口通信

		// 串口通信主过程
		public void commandExec(SerialComPort curComPort, int receiveNum, int receiveTime)
		{
			// 状态复位
			ackIsOk = false;
			dataIsOk = false;
			waitTime = 0;
			readNum = 0;

			// 计算命令校验和
			calcCheckSum();

			// 清除发送/接收缓冲
			curComPort.clearSendBuf();
			curComPort.clearReceiveBuf();

			// 发送命令, 接收反馈
			commandSend(curComPort);
			readNum = commandReceive(curComPort, receiveNum, receiveTime);

			// 判断反馈状况
			if (readNum >= 5)
			{
				ackIsOk = true;
			}
			if (readNum >= receiveNum)
			{
				dataIsOk = true;
			}
		}

		// 通信数据包发送
		public void commandSend(SerialComPort curComPort)
		{
			if (curComPort.Opened == false)
			{
				curComPort.Open();
			}

			byte[] sendData = new byte[frameLength];
			Array.Copy(frameData, sendData, frameLength);
			curComPort.Write(sendData, frameLength);
		}

		// 通信数据包反馈接收
		public int commandReceive(SerialComPort curComPort, int receiveNum, int receiveTime)
		{
			readNum = 0;
			waitTime = 0;

			int ackFlag = 0;
			byte[] ackData = new byte[5];
			int bytesInReceiveBuffer = 0;
			while ((ackFlag == 0 || ackFlag == 2) && (waitTime < receiveTime))
			{
				Thread.Sleep(5);
				waitTime += 5;

				// 读取 ack
				bytesInReceiveBuffer = 0;
				while ((bytesInReceiveBuffer < 5) && (waitTime < receiveTime))
				{
					Thread.Sleep(5);
					waitTime += 5;
					bytesInReceiveBuffer = curComPort.getBytesToRead();
				}

				if (waitTime >= receiveTime)
				{
					//typeLog("[" + curComPort.PortName + "]: Command Receive Timeout");
					return 0;
				}

				curComPort.Read(ref ackData, 5);

				ackFlag = checkAck(ackData);		// 判断 ack
			}

			if (ackFlag == 1)		// 正确 ack
			{
				Array.Copy(ackData, readBuffer, ackData.Length);
				readNum += 5;
			}
			else if (ackFlag == 2)	// 错误 ack
			{
				return 0;
			}
			else if (ackFlag == 3)	// 无 ack, 直接为有效数据
			{
				Array.Copy(ackData, readBuffer, ackData.Length);
				readNum += 5;
			}

			receiveNum -= 5;
			byte[] receiveData = new byte[receiveNum];
			bytesInReceiveBuffer = 0;
			while ((bytesInReceiveBuffer < receiveNum) && (waitTime < receiveTime))
			{
				Thread.Sleep(5);
				waitTime += 5;
				bytesInReceiveBuffer = curComPort.getBytesToRead();
			}
			if (waitTime >= receiveTime)
			{
				//typeLog("[" + curComPort.PortName + "]: Command Receive Timeout");
				return 0;
			}

			curComPort.Read(ref receiveData, (int)receiveNum);
			
			if (ackFlag == 1)
			{
				Array.Copy(receiveData, 0, readBuffer, 5, receiveData.Length);
			}
			else if (ackFlag == 2)
			{
				;
			}
			else if (ackFlag == 3)
			{
				Array.Copy(receiveData, 0, readBuffer, 0, receiveData.Length);
			}
			
			readNum += receiveData.Length;

			return readNum;
		}

		#endregion
		
		#region 辅助函数

		// 打印日志
		public void typeLog(string str)
		{
			Console.Out.WriteLine(str);
		}

		// 计算发送校验
		public void calcCheckSum()
		{
			uint crcValue = 0;
			for (int i = 0; i < frameLength - 1; i++)
			{
				crcValue = crcValue + frameData[i];
			}
			frameData[frameLength - 1] = mod256(256 - (crcValue % 256));
		}

		// 检查 ack
		public int checkAck(byte[] ackData)
		{
			// 数据不足
			if (ackData.Length != 5)
			{
				return 0;
			}
			// 正确 ack
			else if (ackData[0] == 0xaa && ackData[1] == 0x55 && ackData[2] == 0x0a && ackData[3] == 0x01 && ackData[4] == 0xf6)
			{
				return 1;
			}
			// 错误 ack
			else if (ackData[0] == 0xaa && ackData[1] == 0x55 && ackData[2] == 0x0a && ackData[3] == 0x03 && ackData[4] == 0xf4)
			{
				return 2;
			}
			// 无 ack, 直接为有效数据
			else
			{
				return 3;
			}
		}

		// mod256
		public byte mod256(uint i)
		{
			return (byte)(0xff & (i % 256));
		}

		// mod256
		public byte mod256(int i)
		{
			return (byte)(0xff & (i % 256));
		}

		// mod64
		public byte mod64(uint i)
		{
			return (byte)(0x3f & (i % 64));
		}

		// mod64
		public byte mod64(int i)
		{
			return (byte)(0x3f & (i % 64));
		}

		// int -> string
		public string chr(int i)
		{
			return ((char)i).ToString();
		}

		#endregion
	}

	public class ComMainPort
	{
		public static int Unknown = 100;

		public static int U = 0;

		public static int D = 1;
	}
}
